/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2012-1-6
/// Purpose: 		程序数据类型和数据库映射类文件
/// ****************************************************************************
/// Modify By		Date			Remark
/// Robin           2012-2-3        修改对Identity字段的支持，以便针对不同的数据库生成字段
/// Robin           2012-6-14       增加对匿名类初始化的支持
/// Robin           2012-6-14       支持只查询部分字段，但生成完整的类
/// Robin           2013-4-16       增加ignoreAutoIncrement属性，以便对自增列进行插入和更新
/// Robin           2013-4-30       增加属性访问的Delegate,提高运行性能
/// ****************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RB.DataAccess.Attributes;
using System.Reflection;
using RB.DataAccess.Configuration;
using System.Linq.Expressions;
using System.Data;

namespace RB.DataAccess
{
    /// <summary>
    /// 数据类和数据表之间(ORM)的映射关系
    /// </summary>
    /// <typeparam name="T">数据类类型</typeparam>
    public static class TypeMapper<T>
    {
        static TypeMapper()
        {
            Type t = typeof(T);

            _hasDefaultConstructor = ClassConstructorFactory.HasDefaultConstructor(t);
            _constructor = ClassConstructorFactory.Create<T>();
            //GetDefaultConstructor(t);

            _tableName = ExtractTableName(t);
            _queryName = ExtractQueryName(t);

            PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.GetProperty);
            _tableFieldMap = new Dictionary<string, Tuple<string, PropertyInfo, IMemberGetter>>();
            _queryFieldMap = new Dictionary<string, Tuple<PropertyInfo, IMemberSetter>>();
            _queryPropertyFieldMap = new Dictionary<string, string>();
            
            ExtractFieldMap(properties);

            //if (!_hasDefaultConstructor)
            //    GetNonDefaultConstructor(t);
        }

        /// <summary>
        /// 初始化类型对应的查询名称和表名称
        /// </summary>
        #region 初始化类型对应的查询名称和表名称

        private static string _tableName;

        private static string ExtractTableName(Type type)
        {
            object[] attrs = type.GetCustomAttributes(typeof(TableNameAttribute), false);
            if (attrs != null && attrs.Length > 0)
                return ((TableNameAttribute)attrs[0]).Value;
            else
                return type.Name;
        }

        private static string _queryName;

        private static string ExtractQueryName(Type type)
        {
            object[] attrs = type.GetCustomAttributes(typeof(QueryNameAttribute), false);
            if (attrs != null && attrs.Length > 0)
                return ((QueryNameAttribute)attrs[0]).Value;
            else
            {
                return _tableName;
            }
        }

        #endregion 初始化类型对应的查询名称和表名称


        #region 初始化字段和类属性的映射关系

        /// <summary>
        /// 用于通过数据库字段名反向查找Property
        /// 键值为FieldName
        /// </summary>
        private static Dictionary<string, Tuple<PropertyInfo, IMemberSetter>> _queryFieldMap;


        private static Dictionary<string, string> _queryPropertyFieldMap;

        /// <summary>
        /// 用于通过类属性名查找Property
        /// 键值为FieldName
        /// </summary>
        private static Dictionary<string, Tuple<string, PropertyInfo, IMemberGetter>> _tableFieldMap;

        private static PropertyInfo _autoIncrementField = null;

        private static bool _hasAutoIncrementField = false;

        private static bool _ignoreAutoIncrement = false;

        private static void ExtractFieldMap(PropertyInfo[] propertyInfos)
        {
            foreach (PropertyInfo info in propertyInfos)
            {
                bool nonField = false;
                bool readonlyField = false;
                string fieldName = info.Name;
                var attrs = info.GetCustomAttributes(false);
                foreach (var attr in attrs)
                {
                    if (attr is NonFieldAttribute)
                    {
                        nonField = true;
                        break;
                    }
                    else if (attr is ReadOnlyFieldAttribute)
                    {
                        readonlyField = true;
                    }
                    else if (attr is AutoIncrementAttribute)
                    {
                        _autoIncrementField = info;
                        _hasAutoIncrementField = true;
                    }
                    else if (attr is FieldNameAttribute)
                    {
                        fieldName = ((FieldNameAttribute)attr).Value;
                    }
                }
                if (!nonField)
                {
                    if (!readonlyField && info.CanRead)
                    {
                        IMemberGetter getter = null;
                        Type getterType = typeof(PropertyGetter<,>).MakeGenericType(typeof(T), info.PropertyType);
                        getter = (IMemberGetter)Activator.CreateInstance(getterType, info);
                        _tableFieldMap.Add(info.Name, new Tuple<string, PropertyInfo, IMemberGetter>(fieldName, info, getter));
                    }
                    if (info.CanWrite || !_hasDefaultConstructor)
                    {
                        IMemberSetter setter = null;
                        if (_hasDefaultConstructor)
                        {
                            Type setterType = typeof(PropertySetter<,>).MakeGenericType(typeof(T), info.PropertyType);
                            setter = (IMemberSetter)Activator.CreateInstance(setterType, info);
                        }

                        _queryFieldMap.Add(fieldName, new Tuple<PropertyInfo, IMemberSetter>(info, setter));

                        _queryPropertyFieldMap.Add(info.Name, fieldName);
                    }
                }
            }
        }

        #endregion 初始化字段和类属性的映射关系


        #region TableMapper静态属性

        public static string TableName
        {
            get { return _tableName; }
        }

        public static string QueryName
        {
            get { return _queryName; }
        }

        public static Dictionary<string, Tuple<PropertyInfo, IMemberSetter>> QueryFieldMap
        {
            get { return _queryFieldMap; }
        }

        public static Dictionary<string, string> QueryPropertyFieldMap
        {
            get { return _queryPropertyFieldMap; }
        }

        public static Dictionary<string, Tuple<string, PropertyInfo, IMemberGetter>> TableFieldMap
        {
            get { return _tableFieldMap; }
        }

        public static PropertyInfo AutoIncrementField
        {
            get { return _autoIncrementField; }
        }

        public static bool IgnoreAutoIncrement
        {
            get
            {
                return _ignoreAutoIncrement;
            }
            set
            {
                _ignoreAutoIncrement = value;
            }
        }

        public static void UseNativeAutoIncrement()
        {
            if (_hasAutoIncrementField && !_ignoreAutoIncrement)
            {
                _hasAutoIncrementField = false;
                _tableFieldMap.Remove(_autoIncrementField.Name);
            }
        }

        public static string GetQueryFieldName(string propertyName)
        {
            if (_queryPropertyFieldMap.ContainsKey(propertyName))
            {
                return _queryPropertyFieldMap[propertyName];
            }
            return null;
        }

        //public static void GetQueryField(string propertyName, object entity, out string fieldName, out object value)
        //{
        //    if (_queryFieldMap.ContainsKey(propertyName))
        //    {
        //        fieldName = _queryFieldMap[propertyName].Item1;
        //        value = _queryFieldMap[propertyName].Item2.GetValue(entity, null);
        //    }
        //    fieldName = null;
        //    value = null;
        //}

        public static string GetTableFieldName(string propertyName)
        {
            if (_tableFieldMap.ContainsKey(propertyName))
            {
                return _tableFieldMap[propertyName].Item1;
            }
            return null;
        }

        //public static void GetTableField(string propertyName, object entity, out string fieldName, out object value)
        //{
        //    if (_tableFieldMap.ContainsKey(propertyName))
        //    {
        //        fieldName = _tableFieldMap[propertyName].Item1;
        //        value = _tableFieldMap[propertyName].Item2.GetValue(entity, null);
        //    }
        //    fieldName = null;
        //    value = null;
        //}

        #endregion TableMapper静态属性

        #region 动态创建类型实例

        private static bool _hasDefaultConstructor = false;
        private static IClassConstructor _constructor = null;

        //private static Func<T> _defaultConstructor = null;
        //private static ConstructorInfo _constructor = null;

        //private static void GetDefaultConstructor(Type t)
        //{
        //    _constructor = t.GetConstructor(new Type[0]);
        //    if (_constructor != null)
        //    {
        //        _hasDefaultConstructor = true;

        //        NewExpression newExpr = Expression.New(_constructor);
        //        Expression<Func<T>> lambda = Expression.Lambda<Func<T>>(newExpr);
        //        _defaultConstructor = lambda.Compile();
        //    }
        //}

        //private static void GetNonDefaultConstructor(Type t)
        //{
        //    var constructors = t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        //    foreach (var constructor in constructors)
        //    {
        //        if (constructor.GetParameters().Length == QueryFieldMap.Count)
        //        {
        //            _constructor = constructor;
        //            break;
        //        }
        //    }
        //}

        public static T Construct(object[] propertyValues, PropertyInfo[] properties, IMemberSetter[] setters)
        {
            T result = default(T);

            int i = 0;
            if (_hasDefaultConstructor)
            {
                result = (T)_constructor.CreateInstance();

                for (i = 0; i < propertyValues.Length; i++)
                {
                    var propertyInfo = properties[i];
                    //if (propertyInfo != null)
                    //{
                    //    propertyInfo.SetValue(result, TypeSystem.ConvertField(propertyValues[i], propertyInfo.PropertyType), null);
                    //}
                    var setter = setters[i];
                    if (setter != null)
                    {
                        setter.SetValue(result, TypeSystem.ConvertField(propertyValues[i], propertyInfo.PropertyType));
                    }
                }
            }
            else if (_constructor != null)
            {

                for (i = 0; i < propertyValues.Length; i++)
                {
                    propertyValues[i] = TypeSystem.ConvertField(propertyValues[i], properties[i].PropertyType);
                }
                result = (T)_constructor.CreateInstance(propertyValues);
            }
            return result;
        }

        #endregion 动态创建类型实例
    }
}
